#ifndef __SYLAR_CONFIG_H__
#define __SYLAR_CONFIG_H__

#include <memory>
#include <string>
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <yaml-cpp/yaml.h>
#include "log.h"
#include <vector>
#include <list>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include "thread.h"

namespace sylar {

class ConfigVarBase {
public:
  typedef std::shared_ptr<ConfigVarBase> ptr;
  ConfigVarBase(const std::string& name, const std::string& description = "")
    :m_name(name)
    ,m_description(description) {
    // 将 m_name 转换为小写
     std::transform(m_name.begin(), m_name.end(), m_name.begin(), ::tolower);
  }
  virtual ~ConfigVarBase() {}

  const std::string& getName() const { return m_name;}
  const std::string& getDescription() const { return m_description;}

  virtual std::string toString() = 0;
  // 转化字符串为指定类型
  virtual bool fromString(const std::string& val) = 0;
  virtual std::string getTypeName() const = 0;
protected:
  std::string m_name;
  std::string m_description;
};

// F from_type, T to_type
// 将 F 类型转换为 T 类型的仿函数
template<typename F, typename T>
class LexicalCast {
public:
  T operator()(const F& v) {
    return boost::lexical_cast<T>(v);
  }
};

// T 为容器（vector, list, set, unordered_set）时的偏特化
#define XX(container, insert_method) \
  template<typename T> \
  class LexicalCast<std::string, container<T>> { \
  public: \
    container<T> operator()(const std::string& v) { \
      YAML::Node node = YAML::Load(v); \
      typename container<T> vec; \
      std::stringstream ss; \
      for (size_t i = 0; i < node.size(); ++i) { \
        ss.str(""); \
        ss << node[i]; \
        vec.insert_method(LexicalCast<std::string, T>()(ss.str())); \
      } \
      return vec; \
    } \
  }

  XX(std::vector, push_back);
  XX(std::list, push_back);
  XX(std::set, insert);
  XX(std::unordered_set, insert);
#undef XX

// F 为容器（vector, list, set, unordered_set）时的偏特化
#define XX(container) \
  template<typename T> \
  class LexicalCast<container<T>, std::string> { \
  public: \
    std::string operator()(const container<T>& v) { \
      YAML::Node node; \
      for (auto& i : v) { \
        node.push_back(YAML::Load(LexicalCast<T, std::string>()(i))); \
      } \
      std::stringstream ss; \
      ss << node; \
      return ss.str(); \
    } \
  }

  XX(std::vector);
  XX(std::list);
  XX(std::set);
  XX(std::unordered_set);
#undef XX

// T 为 map（std::map, std::unordered_map）时的偏特化
#define XX(map) \
  template<class T> \
  class LexicalCast<std::string, map<std::string, T>> { \
  public: \
      map<std::string, T> operator()(const std::string& v) { \
          YAML::Node node = YAML::Load(v); \
          typename map<std::string, T> m; \
          std::stringstream ss; \
          for(auto it = node.begin(); \
                  it != node.end(); ++it) { \
              ss.str(""); \
              ss << it->second; \
              m.insert(std::make_pair(it->first.Scalar(), \
                          LexicalCast<std::string, T>()(ss.str()))); \
          } \
          return m; \
      } \
  }

  XX(std::map);
  XX(std::unordered_map);
#undef XX

// F 为 map（std::map, std::unordered_map）时的偏特化
#define XX(map) \
  template<class T> \
  class LexicalCast<map<std::string, T>, std::string> { \
  public: \
      std::string operator()(const map<std::string, T>& v) { \
          YAML::Node node(YAML::NodeType::Map); \
          for(auto& i : v) { \
              node[i.first] = YAML::Load(LexicalCast<T, std::string>()(i.second)); \
          } \
          std::stringstream ss; \
          ss << node; \
          return ss.str(); \
      } \
  }

  XX(std::map);
  XX(std::unordered_map);
#undef XX

// FromStr 
//  将 std::string 装换为 T
//  T operator()(const std::string&)
//
// ToStr 
//  将 T 类型转换为 std::string
//  std::string operator()(const T&)
template<typename T, typename FromStr = LexicalCast<std::string, T>
                    , typename ToStr = LexicalCast<T, std::string>>
class ConfigVar : public ConfigVarBase {
public:
  // 读写锁类型
  typedef RWMutex RWMutexType;
  typedef std::shared_ptr<ConfigVar> ptr;
  // 回调函数形式定义
  typedef std::function<void(const T& old_value, const T& new_value)> on_change_cb;

  ConfigVar(const std::string& name
        ,const T& default_value
        ,const std::string& description = "")
    :ConfigVarBase(name, description)
    ,m_val(default_value) {
  }

  std::string toString() override {
    try {
      // return boost::lexical_cast<std::string>(m_val);
      RWMutexType::ReadLock lock(m_mutex);
      return ToStr()(m_val);
    } catch (std::exception& e) {
      SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::toString exception"
          << e.what() << " convert: " << typeid(m_val).name() << " to string";
    }
    return "";
  }

  bool fromString(const std::string& val) override {
    try {
      // m_val = boost::lexical_cast<T>(val);
      setValue(FromStr()(val));
    } catch (std::exception& e) {
      SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "ConfigVar::toString exception"
        << e.what() << " convert: string to " << typeid(m_val).name()
        << " - " << val;
    }
    return false;
  }

  const T getValue() { 
    RWMutexType::ReadLock lock(m_mutex);
    return m_val;
  }

  void setValue(const T& v) { 
    {
      RWMutexType::ReadLock lock(m_mutex);
      if (v == m_val) {
        return;
      }
      for (auto& p : m_cbs) {
        p.second(m_val, v);
      }
    }
    RWMutexType::WriteLock lock(m_mutex);
    m_val = v;
  }
  std::string getTypeName() const override { return typeid(T).name(); }

  // 添加、删除、清空、获取回调函数等操作
  uint64_t addListener(on_change_cb cb) {
    static uint64_t s_fun_id = 0;
    RWMutexType::WriteLock lock(m_mutex);
    m_cbs[s_fun_id++] = cb;
    return s_fun_id;
  }
  void delListener(uint64_t key) {
    RWMutexType::WriteLock lock(m_mutex);
    m_cbs.erase(key);
  }
  void clearListener() {
    RWMutexType::WriteLock lock(m_mutex);
    m_cbs.clear();
  }

  on_change_cb getListener(uint64_t key) {
    RWMutexType::ReadLock lock(m_mutex);
    auto it = m_cbs.find(key);
    return it == m_cbs.end() ? nullptr : it->second;
  }
private:
  // 读写锁
  RWMutexType m_mutex;
  T m_val;
  // 变更回调函数
  // uint64_t key  要求唯一，一般可以用 hash
  std::map<uint64_t, on_change_cb> m_cbs; // 由于 function 无法比较，故需要使用一个 key 来指定 function
};

// 用于管理
class Config {
public:
  typedef std::map<std::string, ConfigVarBase::ptr> ConfigVarMap;
  typedef RWMutex RWMutexType;

  template<typename T>
  static typename ConfigVar<T>::ptr Lookup(const std::string& name,
        const T& default_value, const std::string& description = "") {
    RWMutexType::WriteLock lock(GetMutex());
    auto it = GetDatas().find(name);
    if (it != GetDatas().end()) {
      auto tmp = std::dynamic_pointer_cast<ConfigVar<T>>(it->second);
      if (tmp) {
        SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exists";
        return tmp;
      } else {
        SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exists but type not "
          << typeid(T).name() << " real_type=" << it->second->getTypeName()
          << " " << it->second->toString();
        return nullptr;
      }
    }

    if(name.find_first_not_of("abcdefghikjlmnopqrstuvwxyz._012345678")
        != std::string::npos) {
      SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name invalid " << name;
      throw std::invalid_argument(name);
    }

    typename ConfigVar<T>::ptr v(new ConfigVar<T> (name, default_value, description));
    GetDatas()[name] = v;
    return v;
  }

  template<typename T>
  static typename ConfigVar<T>::ptr Lookup(const std::string& name) {
    RWMutexType::ReadLock lock(GetMutex());
    auto it = GetDatas().find(name);
    if(it == GetDatas().end()) {
      return nullptr;
    }
    return std::dynamic_pointer_cast<ConfigVar<T> >(it->second);
  }

  // 加载配置
  static void LoadFromYaml(const YAML::Node& root);
  // 查找 GetDatas() 中的相关配置项
  static ConfigVarBase::ptr LookupBase(const std::string& name);
  // 转换 map，用于调试等
  static void Visti(std::function<void(ConfigVarBase::ptr)> cb);
private:
  static ConfigVarMap& GetDatas() {
    static ConfigVarMap s_datas;
    return s_datas;
  }
  
  static RWMutexType& GetMutex() {
    static RWMutexType s_mutex;
    return s_mutex;
  }
};

}

#endif
